﻿#include "stdafx.h"
#include "sbcs.h"
#include "extend/sbvalues.h"
#if _SB_UNIX_
#include <sys/types.h>
#include <sys/param.h>
#include <sys/utsname.h>
#endif
#if _SB_BSD_ || _SB_APPLE_
#include <sys/sysctl.h>
#endif
#if _SB_LINUX_
#include <sys/sysinfo.h>
#endif
#if _SB_ANDROID_
#include "cpu-features.h"
#endif
#if __GNUC__ && _SB_X86_
#include <cpuid.h>
#endif

#if _SB_WINDOWS_
#ifndef _WIN32_WINNT_WIN8
#define _WIN32_WINNT_WIN8 0x0602
#endif
#ifndef _WIN32_WINNT_WINBLUE
#define _WIN32_WINNT_WINBLUE 0x0603
#endif
#endif

//////////////////////////////////////////////////////////////////////////
// 기본 정보
static kSysInfo k_impl_si;

static void _k_si_query(kSysInfo* p, kcham netquery);

// 초기화
void _k_si_init(void)
{
	_k_si_query(&k_impl_si, FALSE);
}

// CPU ID
#if _SB_X86_
#if _MSC_VER
// OK
#define sb_cpuid	__cpuid
#elif __GNUC__
// OK
static void sb_cpuid(int* pn, int n)
{
	unsigned int na=0, nb=0, nc=0, nd=0;
	__get_cpuid(n, &na, &nb, &nc, &nd);
	pn[0] = (int)na;
	pn[1] = (int)nb;
	pn[2] = (int)nc;
	pn[3] = (int)nd;
}
#else
// NO!
#error not support compiler. find another '__cpuid' method.
#endif
#endif

// 윈도우 버전 판독, VS2013 - 'VersionHelpers.h' 변경 함
#if _SB_WINDOWS_ && _SB_WINDOWS_DESKTOP_
static kcham TestWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor, int* major, int* minor, int* sp)
{
	OSVERSIONINFOEXW osvi = {sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0};
	DWORDLONG        const dwlConditionMask =
		VerSetConditionMask(
		VerSetConditionMask(
		VerSetConditionMask(
		0, VER_MAJORVERSION, VER_GREATER_EQUAL),
		VER_MINORVERSION, VER_GREATER_EQUAL),
		VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
	osvi.dwMajorVersion = wMajorVersion;
	osvi.dwMinorVersion = wMinorVersion;
	osvi.wServicePackMajor = wServicePackMajor;
	if (!VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask))
		return FALSE;
	else
	{
		*major = wMajorVersion;
		*minor = wMinorVersion;
		*sp = wServicePackMajor;
		return TRUE;
	}
}

static kcham TestWindowsXPOrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0, major, minor, sp);
}

static kcham TestWindowsXPSP1OrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1, major, minor, sp);
}

static kcham TestWindowsXPSP2OrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2, major, minor, sp);
}

static kcham TestWindowsXPSP3OrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3, major, minor, sp);
}

static kcham TestWindowsVistaOrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0, major, minor, sp);
}

static kcham TestWindowsVistaSP1OrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1, major, minor, sp);
}

static kcham TestWindowsVistaSP2OrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2, major, minor, sp);
}

static kcham TestWindows7OrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0, major, minor, sp);
}

static kcham TestWindows7SP1OrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1, major, minor, sp);
}

static kcham TestWindows8OrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0, major, minor, sp);
}

static kcham TestWindows8Point1OrGreater(int* major, int* minor, int* sp)
{
	return TestWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0, major, minor, sp);
}

kcham TestWindowsServer()
{
	OSVERSIONINFOEXW osvi = {sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0, 0, VER_NT_WORKSTATION};
	DWORDLONG const dwlConditionMask = VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL);
	return !VerifyVersionInfoW(&osvi, VER_PRODUCT_TYPE, dwlConditionMask);
}
#endif


//////////////////////////////////////////////////////////////////////////
// 시스템 정보

// cpu 정보
static void _k_si_test_cpu(kSysInfo* p)
{
#if _SB_X86_
	int nn[4] = {-1};
	kuint nids, nexids, i;

	// 정보
	sb_cpuid(nn, 0);

	//
	nids = nn[0];

	// 벤더 스트링
#if _MSC_VER
	*((int*)(p->cpu.vendor + 0)) = nn[1];
	*((int*)(p->cpu.vendor + 4)) = nn[3];
	*((int*)(p->cpu.vendor + 8)) = nn[2];
#else
	memcpy(p->cpu.vendor + 0, &nn[1], sizeof(int));
	memcpy(p->cpu.vendor + 4, &nn[3], sizeof(int));
	memcpy(p->cpu.vendor + 8, &nn[2], sizeof(int));
#endif

	// 루프
	for (i = 0; i <= nids; i++)
	{
		sb_cpuid(nn, i);

		// 모델 형식
		if (i == 1)
		{
			p->cpu.step = (kbyte)(nn[0] & 0xF);
			p->cpu.model = (kbyte)((nn[0] >> 4) & 0xF);
			p->cpu.family = (kbyte)((nn[0] >> 8) & 0xF);
			p->cpu.type = (kbyte)((nn[0] >> 12) & 0x3);
			p->cpu.ext_model = (kbyte)((nn[0] >> 16) & 0xF);
			p->cpu.ext_family = (kbyte)((nn[0] >> 20) & 0xFF);

			p->cpu.brand_id = (kbyte)(nn[1] & 0xFF);
			p->cpu.apic_id = (kbyte)((nn[1] >> 24) & 0xFF);
			p->cpu.cc[0] = ((nn[1] >> 8) & 0xFF) * 8;

			p->cpu.count = (nn[1] >> 16) & 0xF;

			if (nn[2] & 0x1)		p->cpu.ft[0] |= KSIPTF_X86_0_SSE3;
			if (nn[2] & 0x8)		p->cpu.ft[0] |= KSIPTF_X86_0_MONWAIT;   // MONITOR_MWAIT
			if (nn[2] & 0x10)		p->cpu.ft[0] |= KSIPTF_X86_0_CPLDBG;    // CPLQualifiedDebugStore
			if (nn[2] & 0x100)		p->cpu.ft[0] |= KSIPTF_X86_0_THMMON2;   // ThermalMonitor2
			if (nn[2] & 0x20)		p->cpu.ft[0] |= KSIPTF_X86_0_VMX;
			if (nn[3] & 0x1)		p->cpu.ft[0] |= KSIPTF_X86_0_FPU;
			if (nn[3] & 0x2)		p->cpu.ft[0] |= KSIPTF_X86_0_VME;
			if (nn[3] & 0x10)		p->cpu.ft[0] |= KSIPTF_X86_0_TSC;
			if (nn[3] & 0x40)		p->cpu.ft[0] |= KSIPTF_X86_0_PAE;
			if (nn[3] & 0x200)		p->cpu.ft[0] |= KSIPTF_X86_0_APIC;
			if (nn[3] & 0x1000)		p->cpu.ft[0] |= KSIPTF_X86_0_MTRR;
			if (nn[3] & 0x10000)	p->cpu.ft[0] |= KSIPTF_X86_0_PAT;
			if (nn[3] & 0x20000)	p->cpu.ft[0] |= KSIPTF_X86_0_PSE36;
			if (nn[3] & 0x200000)	p->cpu.ft[0] |= KSIPTF_X86_0_DTES;
			if (nn[3] & 0x800000)	p->cpu.ft[0] |= KSIPTF_X86_0_MMX;
			if (nn[3] & 0x2000000)	p->cpu.ft[0] |= KSIPTF_X86_0_SSE;
			if (nn[3] & 0x4000000)	p->cpu.ft[0] |= KSIPTF_X86_0_SSE2;
			if (nn[3] & 0x10000000)	p->cpu.ft[0] |= KSIPTF_X86_0_HTT;
			if (nn[3] & 0x40000000)	p->cpu.ft[0] |= KSIPTF_X86_0_IA64;
			if (nn[2] & (1 << 2))	p->cpu.ft[1] |= KSIPTF_X86_1_DTES64;
			if (nn[2] & (1 << 5))	p->cpu.ft[1] |= KSIPTF_X86_1_VMX;
			if (nn[2] & (1 << 6))	p->cpu.ft[1] |= KSIPTF_X86_1_SMX;
			if (nn[2] & (1 << 7))	p->cpu.ft[1] |= KSIPTF_X86_1_EST;
			if (nn[2] & (1 << 9))	p->cpu.ft[1] |= KSIPTF_X86_1_SSSE3;
			if (nn[2] & (1 << 19))	p->cpu.ft[1] |= KSIPTF_X86_1_SSE41;
			if (nn[2] & (1 << 20))	p->cpu.ft[1] |= KSIPTF_X86_1_SSE42;
		}
	}

	// 확장 정보
	sb_cpuid(nn, 0x80000000);
	nexids = nn[0];

	// 루프
	for (i = 0x80000000; i <= nexids; i++)
	{
		sb_cpuid(nn, i);

		// AMD 스페샬
		if (i == 0x80000001)
		{
			if (nn[3] & 0x40000)	p->cpu.ft[0] |= KSIPTF_X86_0_MMXPLUS;
			if (nn[3] & 0x40000000)	p->cpu.ft[0] |= KSIPTF_X86_0_3DNOWPLUS;
			if (nn[3] & 0x80000000)	p->cpu.ft[0] |= KSIPTF_X86_0_3DNOW;
			if (nn[3] & (1 << 20))	p->cpu.ft[1] |= KSIPTF_X86_1_DEP;
			if (nn[3] & (1 << 29))	p->cpu.ft[1] |= KSIPTF_X86_1_AMD64;
			if (nn[2] & (1 << 0))	p->cpu.ft[1] |= KSIPTF_X86_1_LAHF;
		}
		// 브랜드 정보 설명
		else if (i == 0x80000002)
			memcpy(p->cpu.desc, nn, sizeof(nn));
		else if (i == 0x80000003)
			memcpy(p->cpu.desc + 16, nn, sizeof(nn));
		else if (i == 0x80000004)
			memcpy(p->cpu.desc + 32, nn, sizeof(nn));
		// 캐시 정보
		else if (i == 0x80000006)
		{
			p->cpu.cc[1] = nn[2] & 0xFF;             // Cache line size
			p->cpu.cc[2] = (nn[2] >> 12) & 0xF;      // L2 associativity
			p->cpu.cc[3] = (nn[2] >> 16) & 0xFFFF;   // Cache size K
		}
	}

	// 브랜드 정보 정리
	if (p->cpu.desc[0])
		k_strtrim(p->cpu.desc);

	// CPU 타입
	p->cpu.cpu = KSIPT_X86;
#elif _SB_ANDROID_
	int family = (int)android_getCpuFamily();

	switch (family)
	{
		case ANDROID_CPU_FAMILY_ARM:
			p->cpu.cpu = KSIPT_ARM;
			p->cpu.ft[0] = (int)android_getCpuFeatures();
			k_strcpy(p->cpu.vendor, "ARM");
			break;

		case ANDROID_CPU_FAMILY_X86:
			p->cpu.cpu = KSIPT_X86;
			k_strcpy(p->cpu.vendor, "X86");
			break;

		default:
			p->cpu.cpu = KSIPT_UNKNOWN;
			k_strcpy(p->cpu.vendor, "unknown");
			break;
	}

	k_strcpy(p->cpu.desc, "Android AP");
#elif _SB_ARM_
	// WinRT, Android, iOS, Kibot
	p->cpu.cpu = KSIPT_ARM;
	k_strcpy(p->cpu.vendor, "ARM");
	k_strcpy(p->cpu.desc, "unknown");
#else
#error unknown archtecture for systerm information
#endif	// _SB_???_

	// 개수
#if _SB_WINDOWS_ && _SB_WINDOWS_DESKTOP_
	K_STMT_BEGIN{
		SYSTEM_INFO si;
		GetSystemInfo(&si);
		p->cpu.count = (int)si.dwNumberOfProcessors;
	}K_STMT_END;
#elif _SB_ANDROID_
	p->cpu.count = (int)android_getCpuCount();
#elif _SB_LINUX_ || _SB_APPLE_
	// 리눅스, OSX 1.4(타이거) 이상
	//	get_nprocs()
	p->cpu.count = sysconf(_SC_NPROCESSORS_ONLN);
#elif _SB_BSD_ || _SB_APPLE_
	// BSD(FreeBSD), OSX 1.3 이하
	//	int i=0;
	//	size_t si=sizeof(i);
	//	p->cpu.count=(sysctlbyname("hw.ncpu", &i, &si, NULL, 0)) ? 1 : i;
	K_STMT_BEGIN{
		int sm[4] = {CTL_HW,};
		int cnt;
		size_t size = sizeof(cnt);

#ifdef HW_AVAILCPU
		sm[1] = HW_AVAILCPU;
		sysctl(sm, 2, &cnt, &size, NULL, 0);

		if (cnt >= 1)
			p->cpu.cpu = cnt;
		else
#endif
		{
			sm[1] = HW_NCPU;
			sysctl(sm, 2, &cnt, &size, NULL, 0);

			p->cpu.cpu = cnt < 1 ? 1 : cnt;
		}
	}K_STMT_END;
#elif _SB_IOS_
	// OBJC 코드임
	//NSUInteger a = [[NSProcessInfo processInfo] processorCount];
	//NSUInteger b = [[NSProcessInfo processInfo] activeProcessorCount];
#endif
}

// 메모리
static void _k_si_test_mem(kSysInfo* p)
{
#if _SB_WINDOWS_ && _MSC_VER && _SB_WINDOWS_DESKTOP_
	MEMORYSTATUSEX me;
	me.dwLength = sizeof(MEMORYSTATUSEX);
	GlobalMemoryStatusEx(&me);
	p->mem.phy = me.ullTotalPhys;
	p->mem.left = me.ullAvailPhys;
#elif _SB_WINDOWS_ && (defined(__CYGWIN__) || defined(__CYGWIN32__))
	MEMORYSTATUS ms;
	ms.dwLength = sizeof(MEMORYSTATUS);
	GlobalMemoryStatus(&ms);
	p->mem.phy = ms.dwTotalPhys;
	p->mem.left = ms.dwAvailPhys;
#elif _SB_UNIX_
#	if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
	static int sm[2]=
	{
		CTL_HW,
#if defined(HW_MEMSIZE)
		// OSX
		HW_MEMSIZE
#elif defined(HW_PHYSMEM64)
		// NetBSD, OpenBSD
		HW_PHYSMEM64
#endif
	};
	int64_t size = 0;
	size_t len = sizeof(int64_t);
	if (sysctl(sm, 2, &size, &len, NULL, 0) == 0)
		p->mem.phy = size;
#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
	// FreeBSD, OpenBSD, Linux, Solaris
	p->mem.phy = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE)
	// 그 밖에
	p->mem.phy = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE);
#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
	// DragonFly BSD, FreeBSD, NetBSD, OpenBSD, OSX
	static int sm[2]=
	{
		CTL_HW,
#if defined(HW_REALMEM)
		// FreeBSD
		HW_REALMEM
#elif defined(HW_PYSMEM)
		// ETC.
		HW_PHYSMEM
#endif
	};
	int32_t size = 0;
	size_t len = sizeof(int32_t);
	if (sysctl(sm, 2, &size, &len, NULL, 0) == 0)
		p->mem.phy = size;
#endif
#else
	// 모름 안됨... 패스
#endif
}

// 네트웍
static void _k_si_test_net(kSysInfo* p)
{
#if _SB_WINDOWS_ && _SB_WINDOWS_DESKTOP_
	kwchar wz[64];
	DWORD dw = 64 - 1;

	GetComputerName(wz, &dw);
	k_conv_utf16_to_utf8(p->net.host, 64 - 1, wz, dw);
#elif _SB_UNIX_
	struct utsname un;

	if (uname(&un) == 0)
		k_strncpy(p->net.host, un.nodename, 64 - 1);
	else
		k_nar_look_host_name(p->net.host, 64 - 1);
#endif
}

// 플랫폼
static void _k_si_test_platform(kSysInfo* p)
{
#if _SB_WINDOWS_
	kcham server;
	kint major, minor, sp;
	char desc[64];

	// 기본 정보
	p->os.os = KSIPF_WINDOWS;

#if _SB_WINDOWS_DESKTOP_
	// 버전 판독
	if (TestWindows8Point1OrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_8_1;
	else if (TestWindows8OrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_8;
	else if (TestWindows7SP1OrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_7;
	else if (TestWindows7OrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_7;
	else if (TestWindowsVistaSP2OrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_VISTA;
	else if (TestWindowsVistaSP1OrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_VISTA;
	else if (TestWindowsVistaOrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_VISTA;
	else if (TestWindowsXPSP3OrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_XP;
	else if (TestWindowsXPSP2OrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_XP;
	else if (TestWindowsXPSP1OrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_XP;
	else if (TestWindowsXPOrGreater(&major, &minor, &sp))
		p->os.sub = KSIPFS_WINDOWS_XP;
	else
		major = minor = sp = 0;

	// 서버
	server = TestWindowsServer();

	// 버전별 처리
	switch (p->os.sub)
	{
		case KSIPFS_WINDOWS_XP:
			k_strcpy(desc, server ? "2003" : "XP");
			break;

		case KSIPFS_WINDOWS_VISTA:
			k_strcpy(desc, server ? "2008" : "Vista");
			break;

		case KSIPFS_WINDOWS_7:
			k_strcpy(desc, server ? "2008 R2" : "7");
			break;

		case KSIPFS_WINDOWS_8:
			k_strcpy(desc, server ? "2012" : "8");
			break;

		case KSIPFS_WINDOWS_8_1:
			k_strcpy(desc, server ? "2012 R2" : "8.1");
			break;

		default:
			k_strcpy(desc, "UNKNOWN");
			break;
	}

	// 플래그
	if (server)
		p->os.ft |= KSIPFF_SERVER;

	if (GetSystemMetrics(SM_DBCSENABLED))
		p->os.ft |= KSIPFF_DBCS;

	if (sizeof(size_t) == 8)
		p->os.ft |= KSIPFF_64BIT;

	// 설명 만들기
	if (major == 0)
		k_snprintf(p->os.desc, 64 - 1, "Microsoft Windows %s", desc);
	else if (sp == 0)
		k_snprintf(p->os.desc, 64 - 1, "Microsoft Windows %s (Version %d.%d)", desc, major, minor);
	else
		k_snprintf(p->os.desc, 64 - 1, "Microsoft Windows %s SP%d (Version %d.%d)", desc, sp, major, minor);
#else
	k_snprintf(p->os.desc, 64 - 1, "Microsoft Windows NoDeskTop");
#endif
#elif _SB_UNIX_
#if _SB_ANDROID_
	p->os.os = KSIPF_ANDROID;
	k_strcpy(p->os.desc, "Google ANDROID");
#else
	struct utsname un;

	if (uname(&un) != 0)
	{
		// 찾을 수가 없음
		p->os.os = KSIPF_UNKNOWN;
		return;
	}

	if (k_strcmp(un.sysname, "FeeBSD") == 0)
		p->os.os = KSIPF_FREEBSD;
	else if (k_strcmp(un.sysname, "OpenBSD") == 0)
		p->os.os = KSIPF_OPENBSD;
	else if (k_strcmp(un.sysname, "Linux") == 0)
		p->os.os = KSIPF_LINUX;
	else if (k_strcmp(un.sysname, "darwin") == 0)
		p->os.os = KSIPF_OSX;
	else if (k_strcmp(un.sysname, "cygwin") == 0 || k_strcmp(un.sysname, "interix") == 0) // 확인해봐야함
		p->os.os = KSIPF_POSIX;
	else
		p->os.os = KSIPF_UNKNOWN;

	k_snprintf(p->os.desc, 64 - 1, "%s %s : %s", un.sysname, un.release, un.machine);
#endif
#endif
}

// 쿼리
static void _k_si_query(kSysInfo* p, kcham netquery)
{
	k_zero_1(p);
	_k_si_test_cpu(p);
	_k_si_test_mem(p);
	_k_si_test_platform(p);

	if (netquery)
		_k_si_test_net(p);

	if (p->cpu.count == 0)
		p->cpu.count = 1;

	if (!*p->cpu.desc)
		k_strcpy(p->cpu.desc, "unknown processor");

	if (!*p->cpu.vendor)
		k_strcpy(p->cpu.vendor, "unknown vendor");

	if (!*p->net.host)
		k_strcpy(p->net.host, "unknown host");
}

// 출력 정보
#if _SB_X86_
static const char* _k_siv_cft0[] =
{
	"\0 VMX ", "\0 FPU ", "VME ", "\0 TSC ", "PAE ", "APIC ", "\0 MTRR ",
	"PAT ", "\0 PSE36 ", "\0 DTES ", "MMX ", "MMX+ ", "3DNow ", "3DNow+ ",
	"SSE ", "SSE2 ", "SSE3 ", "\0 MONWT ", "\0 THMN2 ", "\0 CPDBG ", "HTT ", "\0 IA64 ",
};
static const char* _k_siv_cft1[] =
{
	"AMD64 ", "\0 LAHF ", "\0 DTES64 ", "\0 SMX ", "\0 VMX ", "\0 EST ", "\0 DEP ",
	"SSSE3 ", "SSE4.1 ", "SSE4.2 "
};
#elif _SB_ANDROID_
static const char* _k_siv_cft0[] =

{
	"ARMv7 ", "VFPv3 ", "NEON ", "\0 LDREX_STREX ",
};
static const char* _k_siv_cft1[] =
{
	"none ",
};
#else
static const char* _k_siv_cft0[] =
{
	"none ",
};
static const char* _k_siv_cft1[] =
{
	"none ",
};
#endif
#if _SB_WINDOWS_
static const char* _k_siv_pft[] =
{
	"DBCS", "Server", "Embedded", "64BIT",
};
#else
static const char* _k_siv_pft[] =
{
	"none ",
};
#endif

// 정보 출력
void k_sibf(const kSysInfo* p)
{
	char sz[1024];
	int i;

	if (!p)
		p = k_sisd();

	k_outputf("Brief system information\n");

	// cpu
	k_outputf(" CPU - %d(s), Family:%X, Model:%X, Stepping:%X, Extend:%X.%X\n",
		p->cpu.count, p->cpu.family, p->cpu.model, p->cpu.step, p->cpu.ext_family, p->cpu.ext_model);
	k_outputf("       %s by %s\n", p->cpu.desc, p->cpu.vendor);

	// cpu cache
	if (p->cpu.cc[0] != 0 && p->cpu.cc[1] != 0 && p->cpu.cc[3] != 0)
		k_outputf("       Internal cache: %d KB, L1: %d KB, L2: %d KB\n", p->cpu.cc[0], p->cpu.cc[1], p->cpu.cc[3]);

	// cpu ft
	for (i = 0, sz[0] = '\0'; i < (kint)K_COUNTOF(_k_siv_cft0); ++i)
	{
		if ((p->cpu.ft[0] & K_BIT(i)) && *_k_siv_cft0[i])
			k_strcat(sz, _k_siv_cft0[i]);
	}

	for (i = 0; i < (kint)K_COUNTOF(_k_siv_cft1); i++)
	{
		if ((p->cpu.ft[1] & K_BIT(i)) && *_k_siv_cft1[i])
			k_strcat(sz, _k_siv_cft1[i]);
	}

	if (sz[0])
		k_outputf("       %s\n", sz);

	// memory
	k_outputf(" MEM - %llu MB / %llu MB\n", p->mem.left / 1048576, p->mem.phy / 1048576);

	// platform
	k_outputf("  OS - %s\n", p->os.desc);

	for (i = 0, sz[0] = '\0'; i < (kint)K_COUNTOF(_k_siv_pft); i++)
	{
		if ((p->os.ft & K_BIT(i)) && *_k_siv_pft[i])
			k_strcat(sz, _k_siv_pft[i]);
	}

	if (sz[0])
		k_outputf("       %s\n", sz);

	// net
	if (*p->net.host)
		k_outputf("HOST - %s\n", p->net.host);
}

// 간이 자료
void k_sibm(const kSysInfo* si, char* buf, ksize_t size)
{
	if (!si)
		si = k_sisd();

	/*
	char cf0[260], of0[260];
	int i;

	for (i = 0, cf0[0] = '\0'; i < (kint)K_COUNTOF(_k_siv_cft0); ++i)
	{
	if (si->cpu.ft[0] & (1 << i))
	k_strcat(cf0, _k_siv_cft0[i]);
	}

	if (cf0[0] == '\0')
	k_strcpy(cf0, "none");
	else
	k_strrtrim(cf0);

	for (i = 0, of0[0] = '\0'; i < (kint)K_COUNTOF(_k_siv_pft0); i++)
	{
	if (si->os.ft[0] & (1 << i))
	k_strcat(of0, _k_siv_pft0[i]);
	}

	if (of0[0] == '\0')
	k_strcpy(of0, "none");
	else
	k_strrtrim(of0);
	*/

	k_snprintf(buf, size, "{%s-%s} {%s}", si->cpu.vendor, si->cpu.desc, si->os.desc);
}

// 쿼리
void k_siqm(kSysInfo* p)
{
	k_init();
	_k_si_query(p, TRUE);
}

// 기본 데이터
const kSysInfo* k_sisd(void)
{
	k_init();
	return &k_impl_si;
}

// 윈도우 버전
int k_sisd_ossub(void)
{
	k_init();
	return k_impl_si.os.sub;
}
